﻿using Cinch;
using FR.SAFTAnalyser.Model;
using FR.SAFTAnalyser.Model.Stocks;
using FR.SAFTAnalyser.ViewModels.Workspace;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;

namespace FR.SAFTAnalyser.SAFT
{
    internal class StockAnalyser
    {
        #region singleton properties

        private static volatile StockAnalyser instance;
        private static object syncRoot = new Object();

        public static StockAnalyser Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        if (instance == null)
                            instance = new StockAnalyser();
                    }
                }

                return instance;
            }
        }

        #endregion

        public string StockFileName { get; set; }

        public StockFile StockFile { get; set; }

        List<Error> mensagensErro;
        public List<Error> MensagensErro
        {
            get
            {
                if (mensagensErro == null)
                    mensagensErro = new List<Error>();
                return mensagensErro;
            }
            set { mensagensErro = value; }
        }

        string fileVersion;
        public string FileVersion
        {
            get { return fileVersion; }
            set
            {
                fileVersion = value;
                Workspace.Instance.VersaoSTOCK = fileVersion;
            }
        }

        /// <summary>
        /// Loads the Stock file
        /// </summary>
        public void LoadFileAsync()
        {
            BackgroundWorker bw = new BackgroundWorker();
            bw.DoWork += bw_DoWork;
            bw.RunWorkerCompleted += bw_RunWorkerCompleted;
            bw.RunWorkerAsync(this.StockFileName);
        }

        /// <summary>
        /// Validate the stock file against the schema file
        /// </summary>
        public void ValidateSchema()
        {
            if (this.StockFile != null && this.StockFile.StockHeader != null)
                ValidateSchema(this.StockFile.StockHeader.FileVersion);
        }
        /// <summary>
        /// Validate the stock file against a specific schema file
        /// </summary>
        public void ValidateSchema(string fileversion)
        {
            BackgroundWorker bw = new BackgroundWorker();
            bw.DoWork += bw_DoWorkSchema;
            bw.RunWorkerCompleted += bw_RunWorkerCompletedSchema;
            bw.RunWorkerAsync(this.StockFile);
        }

        void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled == false && this.StockFile != null)
                Mediator.Instance.NotifyColleaguesAsync<StockFile>(MessageType.STOCK_FILE_OPENED, this.StockFile);
            if (e.Cancelled == false && File.Exists(this.StockFileName))
                Mediator.Instance.NotifyColleaguesAsync<string>(MessageType.OPEN_STOCK_FILE, this.StockFileName);

            if (this.MensagensErro != null && e.Cancelled == false)
                Mediator.Instance.NotifyColleaguesAsync<Error[]>(MessageType.ERROR_FOUND, this.MensagensErro.ToArray());
        }

        void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            this.MensagensErro.Clear();

            if (File.Exists(e.Argument.ToString()))
            {
                string extension = Path.GetExtension(e.Argument.ToString());
                if (extension == ".xml")
                    this.StockFile = XmlSerializer.Deserialize<StockFile>(e.Argument.ToString());
                else if (extension == ".csv")
                    this.StockFile = CsvSerializer.Deserialize(e.Argument.ToString());
                else
                {
                    MensagensErro.Add(new Error { Description = "Ficheiro de stocks com extensão incorrecta." });
                }

                if (this.StockFile != null)
                {
                    //carregar o nome para o titulo
                    Workspace.Instance.Title = this.StockFile.StockHeader.TaxRegistrationNumber;
                    this.FileVersion = this.StockFile.StockHeader.FileVersion ?? "Sem versão";

                    //Do validations on fields
                    ValidateHeader(this.StockFile.StockHeader);
                    if (this.StockFile.Stock != null)
                        ValidateStocks(this.StockFile.Stock);

                    //remove empty messages
                    MensagensErro.RemoveAll(c => c == null || string.IsNullOrEmpty(c.Description));
                }
                else
                {
                    //show error open file
                    MensagensErro.Add(new Error { Description = "Erro ao abrir o ficheiro Xml" });
                }
            }
        }

        void bw_RunWorkerCompletedSchema(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled == false)
                Mediator.Instance.NotifyColleaguesAsync<Error[]>(MessageType.STOCK_SCHEMA_RESULTS, this.MensagensErro.ToArray());
        }

        void bw_DoWorkSchema(object sender, DoWorkEventArgs e)
        {
            ValidaEstruturaXSD(e.Argument.ToString());
        }

        private void ValidaEstruturaXSD(string p)
        {

            try
            {
                XmlSchemaSet schemas = new XmlSchemaSet();
                schemas.Add(null, XmlReader.Create(new StringReader(Properties.Resources.Stock_1_2)));

                XDocument doc = XDocument.Load(this.StockFileName);
                doc.Validate(schemas, (o, e) =>
                {
                    this.MensagensErro.Add(new Error { Description = e.Message, TypeofError = typeof(SchemaResults) });
                });
            }
            catch (Exception error)
            {
                // XML Validation failed
                this.MensagensErro.Add(new Error { Description = string.Format("Mensagem de erro: {0}", error.Message), TypeofError = typeof(SchemaResults) });
            }
        }

        private void ValidateStocks(Stock[] stocks)
        {
            if (stocks != null && stocks.Length > 0)
            {
                var duplicated = from s in stocks
                                 group s by s.ProductCode into pc
                                 where pc.Count() > 1
                                 select new { codigo = pc.Key, quantidade = pc.Count() };

                foreach (var d in duplicated)
                {
                    string pk = (from s in stocks where s.ProductCode.IndexOf(d.codigo, StringComparison.OrdinalIgnoreCase) >= 0 select s.Pk).FirstOrDefault();
                    MensagensErro.Add(new Error { Value = d.codigo, Field = "ProductCode", Description = string.Format("O identificador {0} está repetido {1} vezes.", d.codigo, d.quantidade), TypeofError = typeof(Stock), UID = pk });
                }

                foreach (Stock stock in stocks)
                {
                    MensagensErro.Add(stock.ValidateClosingStockQuantity());
                    MensagensErro.Add(stock.ValidateProductCode());
                    MensagensErro.Add(stock.ValidateProductDescription());
                    MensagensErro.Add(stock.ValidateProductNumberCode());
                    MensagensErro.Add(stock.ValidateUnitOfMeasure());
                }
            }
            else
                MensagensErro.Add(new Error { Description = "Não existem stocks neste ficheiro" });
        }

        private void ValidateHeader(StockHeader stockHeader)
        {
            MensagensErro.Add(stockHeader.ValidateFileVersion());
            MensagensErro.Add(stockHeader.ValidateFiscalYear());
            MensagensErro.Add(stockHeader.ValidateTaxRegistrationNumber());
        }
    }
}
